<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">
<!--
    Authors: Roland Bouman, Benedikt Kämpgen
    (Roland.Bouman@gmail.com, http://rpbouman.blogspot.com/, http://code.google.com/p/xmla4js)
	
-->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>LDCX - Linked Data Cubes Explorer</title>
<link rel="stylesheet" type="text/css" href="lib/xmla4js/css/default.css" />
<link type="text/css" rel="stylesheet" href="lib/xmla4js/css/pivot-table.css" />
</head>
<body>
	<div id="header">
		<div>
			<a href="ld-cubes-explorer.html">home</a> | <a
				href="http://www.linked-data-cubes.org/">LD-Cubes Project Page</a> | <a href="http://code.google.com/p/olap4ld/">Olap4ld Code Page</a> | <a href="about.html">about</a>
		</div>
		<h1>
			Linked Data Cubes Explorer (LDCX)
		</h1>
		<p>Use the following steps to analyse a statistical dataset. You want to have an idea of the purpose of this system and to give feedback? Use this <a href="https://docs.google.com/forms/d/1J721_e_eCCHjghLny1ZPoeb4X5g2h3RAetK7te_Vw6I/formResponse" target="_blank">form</a>. Thanks!</p>
	</div>
	<div class="full-col">
		<fieldset>
			<legend>1. Select Dataset</legend>
			<p>Select a statistical dataset from the example list or from general <a href="http://www.w3.org/TR/ld-glossary/#linked-data" target="_blank">Linked Data</a> by inserting the <a href="http://www.w3.org/TR/ld-glossary/#uniform-resource-identifier" target="_blank">URI</a> of an instance of <a href="http://www.w3.org/TR/vocab-data-cube/#dfn-qb-dataset" target="_blank">qb:DataSet</a> into the field. Click "Explore Dataset..." to start exploring the dataset.</p> 
			<p>
			<label
				class="abs">Dataset URL:</label><br /> <input id="dsurl"
				value="http://olap4ld.googlecode.com/git/OLAP4LD-trunk/tests/ssb001/ttl/example.ttl#ds" />
			</p>
			<p>
			</p>
			<button id="button_discover_datasources">Explore Dataset...</button><div style="display: none" id="image_ajax_loader_explore"></div><br />
			<p>
			
			</p>
			<button id="button_show_examples">Show Examples...</button>
			<div style="display: none" id="show_examples_text">
			<br />
			<label class="abs">Examples:</label><br />
			<table cellpadding="0" cellspacing="0" border="1">
			<tr><th>Dataset Description</th><th>URI</th></tr>
			<tr><td>Revenue, discount and more for orders of products by customers in Example SSB dataset</td><td>http://olap4ld.googlecode.com/git/OLAP4LD-trunk/tests/ssb001/ttl/example.ttl#ds</td></tr>
			<tr><td>Gross Domestic Product (GDP) per capita in Purchasing Power Standards (PPS) per Country from Eurostat</td><td>http://olap4ld.googlecode.com/git/OLAP4LD-trunk/tests/estatwrap/tec00114_ds.rdf#ds</td></tr>
			<tr><td>Gross Domestic Product (GDP) per capita in Purchasing Power Standards (PPS) per Country from <a href="http://estatwrap.ontologycentral.com/">Estatwrap</a></td><td>http://estatwrap.ontologycentral.com/id/tec00114#ds</td></tr>
			<tr><td>Employment rate, by sex from Eurostat</td><td>http://olap4ld.googlecode.com/git/OLAP4LD-trunk/tests/estatwrap/tsdec420_ds.rdf#ds</td></tr>
			<tr><td>Employment rate, by sex from <a href="http://estatwrap.ontologycentral.com/">Estatwrap</a></td><td>http://estatwrap.ontologycentral.com/id/tsdec420#ds</td></tr>
			<tr><td>Real GDP growth rate from Eurostat</td><td>http://olap4ld.googlecode.com/git/OLAP4LD-trunk/tests/estatwrap/tsieb020_ds.rdf#ds</td></tr>
			<tr><td>Stock Market Values on 2012-12-12 for Bank of America Corporation from <a href="http://yahoofinancewrap.appspot.com/">Yahoo! Finance Wrap</a></td><td>http://yahoofinancewrap.appspot.com/archive/BAC/2012-12-12#ds</td></tr>
			<tr><td>Historical HCO3 climate data at location AD0514 from <a href="http://smartdbwrap.appspot.com/">SMART DB Wrap</a></td><td>http://smartdbwrap.appspot.com/id/locationdataset/AD0514/HCO3</td></tr>
			<tr><td>Historical Precipitation climate data at location AD0514 from SMART Database</td><td>http://olap4ld.googlecode.com/git/OLAP4LD-trunk/tests/smartdbwrap/AD0514-Q.rdf#ds</td></tr>
			<tr><td>Historical Precipitation climate data at location AD0514 from <a href="http://smartdbwrap.appspot.com/">SMART DB Wrap</a></td><td>http://smartdbwrap.appspot.com/id/locationdataset/AD0514/Q</td></tr>
			<tr><td>Balance Sheet (10-K) for COSTCO WHOLESALE CORP, published on 2010-08-29 from S.E.C. Edgar</td><td>http://olap4ld.googlecode.com/git/OLAP4LD-trunk/tests/edgarwrap/0001193125-10-230379.rdf#ds</td></tr>
			<tr><td>Balance Sheet (10-K) for COSTCO WHOLESALE CORP, published on 2010-08-29 from <a href="http://edgarwrap.ontologycentral.com/">Edgar Wrap</a></td><td>http://edgarwrap.ontologycentral.com/archive/909832/0001193125-10-230379#ds</td></tr>
			<tr><td>AFRAID OF UNEMPLOYMENT, EMPLOYEES of ZA4570 ALLBUS/GGSS 1980-2008 <a href="http://gesis-lod.appspot.com/">GESIS LOD Pilot</a></td><td>http://lod.gesis.org/lodpilot/ALLBUS/ZA4570v590.rdf#ds</td></tr>
			<tr><td>Energy productivity from <a href="http://estatwrap.ontologycentral.com/">Estatwrap</a></td><td>http://estatwrap.ontologycentral.com/id/t2020_rd310#ds</td></tr>	
			<tr><td>Energy dependence from <a href="http://estatwrap.ontologycentral.com/">Estatwrap</a></td><td>http://estatwrap.ontologycentral.com/id/tsdcc310#ds</td></tr>
			<tr><td>Energy intensity from <a href="http://estatwrap.ontologycentral.com/">Estatwrap</a></td><td>http://estatwrap.ontologycentral.com/id/tsdec360#ds</td></tr>
			<tr><td>Greenhouse gas emissions per capita from <a href="http://estatwrap.ontologycentral.com/">Estatwrap</a></td><td>http://estatwrap.ontologycentral.com/id/t2020_rd300#ds</td></tr>
			<tr><td>Share of renewable energy from <a href="http://estatwrap.ontologycentral.com/">Estatwrap</a></td><td>http://estatwrap.ontologycentral.com/id/t2020_31#ds</td></tr>
			<tr><td>People at risk of poverty or social exclusion from <a href="http://estatwrap.ontologycentral.com/">Estatwrap</a></td><td>http://estatwrap.ontologycentral.com/id/t2020_50#ds</td></tr>
			<tr><td>People living in households with very low work intensity from <a href="http://estatwrap.ontologycentral.com/">Estatwrap</a></td><td>http://estatwrap.ontologycentral.com/id/t2020_51#ds</td></tr>
			<tr><td>People at risk of poverty after social transfers <a href="http://estatwrap.ontologycentral.com/">Estatwrap</a></td><td>http://estatwrap.ontologycentral.com/id/t2020_52#ds</td></tr>
			<tr><td>Severely materially deprived people from <a href="http://estatwrap.ontologycentral.com/">Estatwrap</a></td><td>http://estatwrap.ontologycentral.com/id/t2020_53#ds</td></tr>
			<tr><td>Example Star Schema Dataset from <a href="http://pastebin.com/839G2u72">Pastebin</a></td><td>http://pastebin.com/raw.php?i=839G2u72#ds</td></tr>
								
			</table>
			</div>
			<p>
			</p>
			<p>
		<label
				class="abs">Analysed dataset:</label><br />
				</p>
		<div style="display: none">
				<label class="abs">Datasource:</label> <select
					id="select_datasource"></select><br />
			</div>
			<div style="display: none">
				<label class="abs">Catalog:</label> <select id="select_catalog"></select><br />
			</div>
			<div><select id="select_cube"></select><br /></div>
		</fieldset>
	</div>
	<div class="full-col">
		<fieldset>
			<legend>2. Select Measures</legend>
			<p>Click the measures in the unused list and they will be added
				to the used list (and vice-versa).</p>
			<table>
				<tr>
					<th><label for="select_measures_unused">Unused:</label></th>
					<th><label for="select_measures_used">Used:</label></th>
				</tr>
				<tr>
					<td class="select"><select multiple="true"
						id="select_measures_unused"></select></td>
					<td class="select"><select multiple="true"
						id="select_measures_used"></select></td>
				</tr>
			</table>
		</fieldset>
	</div>
	<div class="full-col">
		<form>
			<fieldset>
				<legend>3. Select Dimensions</legend>
				<p>Use the radio buttons to select either Slice, Rows or Columns. To add dimensions to
					a selected group, click the entries on any of the two other groups.
				</p>
				<table>
					<tr>
						<th><input type="radio" name="axis" value="slicer"
							id="axis_slicer" /> <label for="axis_slicer">Sliced Dimensions</label></th>
						<th><input type="radio" name="axis" value="rows"
							id="axis_rows" /> <label for="axis_rows">Row Dimensions</label></th>
						<th><input type="radio" name="axis" value="columns"
							checked="true" id="axis_columns" /> <label for="axis_columns">Column Dimensions</label>
						</th>
					</tr>
					<tr>
						<td class="select"><select multiple="true"
							id="select_hierarchies_slicer"></select></td>
						<td class="select"><select multiple="true"
							id="select_hierarchies_rows"></select></td>
						<td class="select"><select multiple="true"
							id="select_hierarchies_columns"></select></td>
					</tr>
				</table>
			</fieldset>
		</form>
	</div>
	
	<div class="footer">
		<fieldset>
			<legend>Pivot Table</legend>
			<select id="select_empty_cells" style="display: none">
				<option value="none">Allow empty cells</option>
				<option value="all">Suppress empty cells</option>
				<option value="rows">Suppress empty rows</option>
				<option value="cols">Suppress empty columns</option>
			</select>
			<button id="button_render_table">Update Table...</button><div style="display: none" id="image_ajax_loader_update"></div>
			<br/><br/><br/>
			<div id="section_table"></div>
			<br/><br/><br/>
			<button id="button_show_log">Show Log...</button>
		</fieldset>
		<div style="display: none" id="show_log_text">
		<fieldset>
			<legend>MDX</legend>
			<pre id="section_query"></pre>
		</fieldset>
		<fieldset>
			<legend>Log</legend>
			<pre id="section_info"></pre>
		</fieldset>
		</div>
	</div>

<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  //ga('create', 'UA-44915149-1', 'linked-data-cubes.org');
  //ga('send', 'pageview');

</script>

	<script type="text/javascript" src="lib/xmla4js/src/Xmla.js"></script>

	<script type="text/javascript">
	/*
	* Static variables
	*/ 
	var xmlaurl = "http://localhost:8080/xmlaserver-trunk/xmla";
	//var xmlaurl = "http://141.52.218.137:8000/xmlaserver-trunk/xmla";
	//var xmlaurl = "http://www.ldcx.linked-data-cubes.org:8000/xmlaserver-trunk/xmla";
	//var xmlaurl = "http://aifbkos.aifb.uni-karlsruhe.de:8080/xmlaserver-trunk/xmla";
	//var xmlaurl = "http://km.aifb.kit.edu/services/xmlaserver-trunk/xmla";
	
	// Pivot table
	var xsltURL = "lib/xmla4js/xslt/xmla-mdresult.xslt";
	//var xsltURL = "/projects/ldcx/lib/xmla4js/xslt/xmla-mdresult.xslt";
	
	// Error message from XMLA (leave it for now and just have hint to log)
	//var messagexsltURL = "lib/xmla4js/xslt/xmla-message.xslt";
	
		/*
		 *   references to the DOM
		 */
		var button_discover_datasources = document
				.getElementById("button_discover_datasources"), button_render_table = document
				.getElementById("button_render_table"), url = document
				.getElementById("url"), select_datasource = document
				.getElementById("select_datasource"), select_catalog = document
				.getElementById("select_catalog"), select_cube = document
				.getElementById("select_cube"), select_measures_unused = document
				.getElementById("select_measures_unused"), select_measures_used = document
				.getElementById("select_measures_used"), select_hierarchies_slicer = document
				.getElementById("select_hierarchies_slicer"), select_hierarchies_rows = document
				.getElementById("select_hierarchies_rows"), select_hierarchies_columns = document
				.getElementById("select_hierarchies_columns"), section_query = document
				.getElementById("section_query"), section_table = document
				.getElementById("section_table"), image_ajax_loader_explore = document
				.getElementById("image_ajax_loader_explore"), image_ajax_loader_update = document
				.getElementById("image_ajax_loader_update"), select_empty_cells = document
				.getElementById("select_empty_cells"),
		/*
		 *   local caches
		 */
		datasources = null, hierarchies = null, measures = null,
		/*
		 *   misc
		 */
		 
		// Try to run synchronously
		xmla = new Xmla({
			async : true,
			requestTimeout: 300000
		}), dataSource, xslt;

		
		// globally-unique identifiers
		// see http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
		var uuid = guid();
		
		/*
		 *   Functions to populate objects with data from xmla
		 */

		function getSelectValue(select) { //in normal browsers, we can do select.value. Why IE8 wants us to write a funcion like this?
			var value;
			if (select.options.length) {
				value = select.options[select.selectedIndex].value;
			} else {
				value = null;
			}
			return value;
		}

		function populateSelect(config) {
			while (config.select.options.length) {
				config.select.removeChild(config.select.firstChild);
			}
			var size, options = config.options;
			if (options instanceof Array) {
				for ( var option, text, addtext, name = "", i = 0; i < options.length; i++) {
					option = document.createElement("OPTION");
					text = options[i][config.text];
					addtext = options[i][config.addtext];
					name = options[i][config.name];
					if(addtext != null) {
						option.innerHTML = text+"("+addtext+")";	
					} else {
						option.innerHTML = text;
					}
					option.value = name;
					config.select.appendChild(option);
				}
				size = options.length;
			} else if (options instanceof Object) {
				size = 0
				for ( var p in options) {
					if (options.hasOwnProperty(p)) {
						option = document.createElement("OPTION");
						option.innerHTML = p;
						option.value = p;
						config.select.appendChild(option);
					}
					size++
				}
			}
			//setting the size is the only way I could get IE8 to behave.
			//of course, that is not enough becayse that would just be way too easy.
			//no, we have to check ourself too if it was a multi select list.
			//MS, can't you just give up on this browser thing?
			config.select.size = config.select.multiple ? size : 1;
		}
		
		function populateMeasuresSelect(config) {
			while (config.select.options.length) {
				config.select.removeChild(config.select.firstChild);
			}
			var size, options = config.options;
			if (options instanceof Array) {
				for ( var option, text, addtext, name = "", i = 0; i < options.length; i++) {
					option = document.createElement("OPTION");
					text = options[i][config.text];
					addtext = options[i][config.addtext];
					name = options[i][config.name];
					if(addtext != null) {
						//option.innerHTML = "<div title=\""+name+"\">"+text+"("+getAggFunctionName(addtext)+","+name+")"+"</div>";	
						option.innerHTML = text+"("+getAggFunctionName(addtext)+","+name+")";
						//option.innerHTML = name;
					} else {
						option.innerHTML = text;
					}
					option.value = name;
					config.select.appendChild(option);
				}
				size = options.length;
			} else if (options instanceof Object) {
				size = 0
				for ( var p in options) {
					if (options.hasOwnProperty(p)) {
						option = document.createElement("OPTION");
						option.innerHTML = p;
						option.value = p;
						config.select.appendChild(option);
					}
					size++
				}
			}
			//setting the size is the only way I could get IE8 to behave.
			//of course, that is not enough becayse that would just be way too easy.
			//no, we have to check ourself too if it was a multi select list.
			//MS, can't you just give up on this browser thing?
			config.select.size = config.select.multiple ? size : 1;
		}

		function populateChecklist(config) {
			var choices;
			if (config.choices) {
				var choice;
				choices = "<select name=\"${name}\""
						+ " onchange=\"dimension_selected(this)\"" + ">"
				for ( var i = 0; i < config.choices.length; i++) {
					choice = config.choices[i];
					choices += "<option"
							+ (config.checked ? " selected=\"" + config.checked
									+ "\"" : "") + " value=\"" + choice + "\">"
							+ choice + "<\/option>";
				}
				choices += "<\/select>_$tag_____"
			} else {
				choices = "<input name=\"${name}\""
						+ " onclick=\"measure_clicked(this)\""
						+ " type=\"checkbox\"/>"
			}
			for ( var text, str = "", i = 0; i < config.options.length; i++) {
				text = config.options[i][config.text]
				str += "_$tag______$tag__+ text g" + choices + text
						+ "_$tag______$tag______$tag_$tag__";
			}
			config.select.innerHTML = str;
		}

		function populateDatasources(rowset) {
			datasources = rowset.fetchAllAsObject();
			populateSelect({
				select : select_datasource,
				options : datasources,
				text : "DataSourceName",
				name : "DataSourceName"
			});
			rowset.close();
			select_datasource.onchange();
			//select_datasource_onchange();
		}

		function populateCatalogs(rowset) {
			var catalogs = rowset.fetchAllAsObject();
			populateSelect({
				select : select_catalog,
				options : catalogs,
				text : "CATALOG_NAME",
				name : "CATALOG_NAME"
			});
			rowset.close();
			select_catalog.onchange();
		}

		function populateCubes(rowset) {
			var cubes = rowset.fetchAllAsObject();
			populateSelect({
				select : select_cube,
				options : cubes,
				text : "CUBE_CAPTION",
				name : "CUBE_NAME"
			});

			//var mycube = [];
			//mycube[0] = [];
			//mycube[0]["CUBE_NAME"] = encodeUri2Mdx(dsurl.value);
			// var mycube = encodeURIComponent(encodeUri2Mdx(dsurl.value));
			// We manually add the cube to cubes
			//populateSelect({
			//	select : select_cube,
			//	options : mycube,
			//	text : "CUBE_NAME"
			//});

			rowset.close();
			select_cube.onchange();
		}

		function populateMeasures(rowset) {
			measures = rowset.fetchAllAsObject();
			populateMeasuresSelect({
				select : select_measures_unused,
				options : measures,
				text : "MEASURE_CAPTION",
				addtext : "MEASURE_AGGREGATOR",
				name : "MEASURE_NAME"
			});
			
			// I automatically add the first measure to used
			var prefix = "select_measures_";
			var option = select_measures_unused.options[0];
			option.selected = false;
			var select;
			select = select_measures_used;

			select_measures_unused.removeChild(option);
			select_measures_unused.size = select_measures_unused.options.length;
			select.appendChild(option);
			select.size = select.options.length;
			
			rowset.close();

			var datasource = datasources[select_datasource.selectedIndex];

			var properties = {};
			properties[Xmla.PROP_DATASOURCEINFO] = datasource[Xmla.PROP_DATASOURCEINFO];
			properties[Xmla.PROP_CATALOG] = getSelectValue(select_catalog);
			//properties[Xmla.PROP_CATALOG] = "[LdCatalogSchema]";

			var restrictions = {};
			restrictions["CATALOG_NAME"] = getSelectValue(select_catalog);
			restrictions["CUBE_NAME"] = getSelectValue(select_cube);
			xmla.discoverMDHierarchies({
				url : xmlaurl,
				properties : properties,
				restrictions : restrictions
			});
		}
		
		function getAggFunctionName(aggregator) {
			switch (aggregator) {
			case 0:
				return "UNKNOWN";
			case 1:
				return "SUM";
			case 2:
				return "COUNT";
			case 3:
				return "MIN";
			case 4:
				return "MAX";
			case 5:
				return "AVG";
			case 6:
				return "VAR";
			case 7:
				return "STD";
			case 8:
				return "CALCULATED";
			default:
				return "UNKNOWN";
			}
		}

		function populateHierarchies(rowset) {
			//hierarchies = rowset.mapAllAsObject([ "HIERARCHY_NAME" ]);
			hierarchies = rowset.mapAllAsObject([ "HIERARCHY_CAPTION" ]);
			populateSelect({
				select : select_hierarchies_slicer,
				options : hierarchies,
				text : "HIERARCHY_CAPTION",
				name : "HIERARCHY_NAME",
				choices : [ "columns", "rows", "slicer" ],
				checked : "rows"
			});
			
			// I automatically add measures to column and first dimension to row
			
			var prefix = "select_hierarchies_";
			var option = select_hierarchies_slicer.options[select_hierarchies_slicer.options.length-1];
			option.selected = false;
			var form = select_hierarchies_slicer.form;
			var elements = form.elements;
			var axis;
			var axis_buttons = elements["axis"];
			// Column
			axis = axis_buttons[2].value;
			
			var hierarchy = option.value;
			var select_hierarchy = prefix + axis;
			
			select_hierarchies_slicer.removeChild(option);
			hierarchies.size = select_hierarchies_slicer.options.length;
			var target = document.getElementById(select_hierarchy);
			target.appendChild(option);
			target.size = target.options.length;
			
			// First dimension to row
			var prefix = "select_hierarchies_";
			var option = select_hierarchies_slicer.options[0];
			option.selected = false;
			var form = select_hierarchies_slicer.form;
			var elements = form.elements;
			var axis;
			var axis_buttons = elements["axis"];
			// Row
			axis = axis_buttons[1].value;
			
			var hierarchy = option.value;
			var select_hierarchy = prefix + axis;

			select_hierarchies_slicer.removeChild(option);
			select_hierarchies_slicer.size = select_hierarchies_slicer.options.length;
			var target = document.getElementById(select_hierarchy);
			target.appendChild(option);
			target.size = target.options.length;
			
			rowset.close();
		}

		function getMDXSet(select_hierarchies) {
			var hierarchy, tail = "", mdx = "";

			for ( var i = 0; i < select_hierarchies.length; i++) {
				hierarchy = hierarchies[select_hierarchies.options[i].value];
				if (i) {
					mdx += ", ";
				}
				if (select_hierarchies.length - i > 1) {
					mdx += "CrossJoin(";
					tail += ")";
				}
				if (hierarchy["DIMENSION_TYPE"] == Xmla.Rowset.MD_DIMTYPE_MEASURE) {
					if (select_measures_used.options.length > 1) {
						//mdx += "union(";
						mdx += "{";
						for ( var j = 0; j < select_measures_used.length; j++) {
							if (j > 0) {
								mdx += ",";
							}
							mdx += "" + ""
									+ select_measures_used.options[j].value
									+ "";
						}
						//mdx += ")";
						mdx += "}";
					} else {
						mdx += "{" + "" + select_measures_used.options[0].value
								+ "}";
					}
				} else {
					
					if (typeof hierarchy["HIERARCHY_UNIQUE_NAME"] != 'undefined') {
						mdx += "{Members(" + hierarchy["HIERARCHY_UNIQUE_NAME"]
						+ ")}";	
					} else {
						// Use the first in the list
						mdx += "{Members(" + hierarchy[0]["HIERARCHY_UNIQUE_NAME"]
						+ ")}";
					}
					
					
				}
			}
			if (i > 1) {
				mdx += tail;
			}
			return mdx;
		}

		function generateMDX() {  
			// For now, we add NON EMPTY to both
			// see "http://linked-data-cubes.org/index.php/NON_EMPTY_Clause_in_MDX"
			
			var mdx = "SELECT /* $session: "+uuid+" */ NON EMPTY ";
			mdx += getMDXSet(select_hierarchies_columns) + " ON COLUMNS";
			if (select_hierarchies_rows.options.length) {
				mdx += "\n, NON EMPTY " + getMDXSet(select_hierarchies_rows) + " ON ROWS";
			}
			
			// Square brackets are already added by the element.
			mdx += "\nFROM " + getSelectValue(select_cube) + "";
			
			// Change usage of measure used list to add dice if in sliced dimensions
			var measureDimensionInSliced = false;
			for ( var i = 0; i < select_hierarchies_slicer.options.length; i++) {
				var option = select_hierarchies_slicer.options[i];
				if (option.value == "Measures") {
					measureDimensionInSliced = true;
				}
					
			}
			
			if (measureDimensionInSliced) {
				// Create measures dices string set
				var measures_set = "{ ";
				
				// Take the first measure from used
				var option = select_measures_used.options[0];
				
				measures_set += option.value+" }";
				
				mdx += "\nWHERE "+ measures_set;
			}
			
			return mdx;
		}

		function render_table(xmla) {
		
			//xslt init code (once)
			if (!xslt) {
				var xsltParamName = "param-render", xsltParamValue = "";
				if (window.ActiveXObject) {
					/*
					 *   One M$ way, as per example: http://msdn.microsoft.com/en-us/library/ms763679(VS.85).aspx
					 */
					var xsltDoc, xsltTemplate;
					xsltDoc = new ActiveXObject(
							'MSXML2.FreeThreadedDOMDocument.6.0');
					xsltDoc.async = false;
					xsltDoc.load(xsltURL);

					xsltTemplate = new ActiveXObject("Msxml2.XSLTemplate.6.0");
					xsltTemplate.stylesheet = xsltDoc;

					xslt = xsltTemplate.createProcessor();
					xslt.addParameter(xsltParamName, xsltParamValue, "");
				} else {
					/*
					 *   Non-IE code as per example http://www.w3schools.com/xsl/xsl_client.asp
					 */
					var xhr;
					xhr = new XMLHttpRequest();
					xhr.open("GET", xsltURL, false);
					xhr.send("");

					xslt = new XSLTProcessor();
					xslt.importStylesheet(xhr.responseXML);
					xslt.setParameter(null, xsltParamName, xsltParamValue);
				}
			}
			//xslt apply code
			section_table.innerHTML = "";
			var result;
			if (window.ActiveXObject) {
				xslt.input = xmla.responseXML;
				xslt.transform();
				section_table.innerHTML = xslt.output;
			} else if (document.implementation
					&& document.implementation.createDocument) {
				result = xslt.transformToFragment(xmla.responseXML, document);
				section_table.appendChild(result);
			}	
			
			// Display info
			log_xmla(xmla);
		}
		
		function log_xmla(xmla) {
			// Display info
			section_info.innerHTML = "";
			section_info.innerHTML = htmlEscape(xmla.responseText);
		}
		
		function htmlEscape(str) {
		    return String(str)
		            .replace(/&/g, '&amp;')
		            .replace(/"/g, '&quot;')
		            .replace(/'/g, '&#39;')
		            .replace(/</g, '&lt;')
		            .replace(/>/g, '&gt;');
		}
		
		function encodeUri2Mdx(str) {
			//return str;
			str = encodeURIComponent(str)
			str = str.replace(/\./g, "YYY");
			str = str.replace(/-/g, "ZZZ");
			str = str.replace(/%/g, "XXX");	
			return "["+str+"]";
		}

		/*
		 *   Xmla event Handlers
		 */
		function initAjaxLoader() {		
			xmla.addListener({
				events : Xmla.EVENT_GENERAL,
				handler : function(eventName) {
					var display;
					switch (eventName) {
					case Xmla.EVENT_REQUEST:
						display = "";
						break;
					case Xmla.EVENT_SUCCESS:
						break;
					case Xmla.EVENT_ERROR:
						
						image_ajax_loader_explore.innerHTML = "An error occured when loading the dataset. Please see log below for details for why this happened. For instance, the dataset may not fulfil the <a href=\"http://www.w3.org/TR/vocab-data-cube/#wf-rules\" target=\"_blank\">integrity constraints</a>.<br/>";

						break;
					}
					image_ajax_loader_explore.style.display = display;
					image_ajax_loader_update.style.display = display;
				}
			});
		}
		function initDiscoverEvents() {
			xmla.addListener({
				events : Xmla.EVENT_DISCOVER_SUCCESS,
				handler : function(eventName, eventData) {
					var display;
					var requestType = eventData.requestType;				
					switch (requestType) {
					case Xmla.DISCOVER_DATASOURCES:
						populateDatasources(eventData.rowset);
						//image_ajax_loader_explore.innerHTML += "DISCOVER_DATASOURCES <br/>";
						// Start						
						image_ajax_loader_explore.innerHTML += "Loading dataset, measures, dimensions... <br/>Running <a href=\"http://www.w3.org/TR/vocab-data-cube/#normalize-algorithm\" target=\"_blank\">normalisation algorithm</a>, checking <a href=\"http://www.w3.org/TR/vocab-data-cube/#wf-rules\" target=\"_blank\">integrity constraints</a>... <br/><br/><br /> ";
						break;
					case Xmla.DBSCHEMA_CATALOGS:
						populateCatalogs(eventData.rowset);
						//image_ajax_loader_explore.innerHTML += "DBSCHEMA_CATALOGS <br/>";
						break;
					case Xmla.MDSCHEMA_CUBES:
						populateCubes(eventData.rowset);
						//image_ajax_loader_explore.innerHTML += "MDSCHEMA_CUBES <br/>";
						break;
					case Xmla.MDSCHEMA_HIERARCHIES:
						populateHierarchies(eventData.rowset);
						//image_ajax_loader_explore.innerHTML += "MDSCHEMA_HIERARCHIES <br/>";
						
						// Finished, now we can remove loading gif and update table
						
						display = "none";
						image_ajax_loader_explore.style.display = display;
						image_ajax_loader_update.style.display = display;
						
						button_render_table_onclick();
						
						break;
					case Xmla.MDSCHEMA_MEASURES:
						populateMeasures(eventData.rowset);
						//image_ajax_loader_explore.innerHTML += "MDSCHEMA_MEASURES <br/>";
						break;
					}
					log_xmla(xmla);
				}
			});
			
			xmla.addListener({
				events : Xmla.EVENT_DISCOVER_ERROR,
				handler : function(eventName, eventData) {
					log_xmla(xmla);
				}
			});
		}

		function initExecuteEvents() {
			xmla.addListener({
				events : Xmla.EVENT_EXECUTE_SUCCESS,
				handler : function(eventName, eventData, xmla) {
					var display;					
					var properties = eventData.properties;
					switch (properties[Xmla.PROP_FORMAT]) {
					case Xmla.PROP_FORMAT_TABULAR:
						break;
					case Xmla.PROP_FORMAT_MULTIDIMENSIONAL:
												
						render_table(xmla);
						
						// Finished, now we can remove loading gif
						display = "none";
						image_ajax_loader_explore.style.display = display;
						image_ajax_loader_update.style.display = display;
						
						break;
					default:
					}
				}
			});
			
			xmla.addListener({
				events : Xmla.EVENT_EXECUTE_ERROR,
				handler : function(eventName, eventData, xmla) {
					var properties = eventData.properties;
					log_xmla(xmla);
				}
			});
			
		}
		/*
		 *   DOM event Handlers
		 */
		function clear_hierarchies_and_measures() {
			select_measures_used.innerHTML = "";
			select_measures_unused.innerHTML = "";
			select_hierarchies_slicer.innerHTML = "";
			select_hierarchies_rows.innerHTML = "";
			select_hierarchies_columns.innerHTML = "";
		}

		function button_discover_datasources_onclick() {
			
			// Reset Loader Gif
			image_ajax_loader_explore.innerHTML = "<img src=\"lib/xmla4js/css/ajax-loader.gif\" /><br/><br/>";
			
			select_datasource.innerHTML = "";
			select_catalog.innerHTML = "";
			select_cube.innerHTML = "";
			clear_hierarchies_and_measures();
			
			// Track event
			ga('send', 'event', 'button_explore_dataset', uuid, dsurl.value, 1);
			
			// Maybe add here a definition of the "available" datasets?
			// Hopefully, we can also add properties. 
			// Probably not, since getDatabases does not have input parameters.
			xmla.discoverDataSources({
				url : xmlaurl
			});
		}

		function select_datasource_onchange() {
			if (!select_datasource.options.length || !datasources.length) {
				return;
			}
			var datasource = datasources[select_datasource.selectedIndex];

			var properties = {};
			properties[Xmla.PROP_DATASOURCEINFO] = datasource[Xmla.PROP_DATASOURCEINFO];

			xmla.discoverDBCatalogs({
				url : xmlaurl,
				properties : properties
			});
		}

		function select_catalog_onchange() {
			if (!select_catalog.options.length) {
				return;
			}
			var datasource = datasources[select_datasource.selectedIndex];

			var properties = {};
			properties[Xmla.PROP_DATASOURCEINFO] = datasource[Xmla.PROP_DATASOURCEINFO];
			//properties[Xmla.PROP_CATALOG] = getSelectValue(select_catalog);
			properties[Xmla.PROP_CATALOG] = "[LdCatalogSchema]";
			var restrictions = {};
			restrictions["CATALOG_NAME"] = getSelectValue(select_catalog);
			//restrictions["CATALOG_NAME"] = "[LdCatalogSchema]";
			restrictions["CUBE_NAME"] = encodeUri2Mdx(dsurl.value);

			xmla.discoverMDCubes({
				url : xmlaurl,
				properties : properties,
				restrictions : restrictions
			});
		}

		function select_cube_onchange() {
			if (!select_cube.options.length) {
				return;
			}
			clear_hierarchies_and_measures();
			var datasource = datasources[select_datasource.selectedIndex];

			var properties = {};
			properties[Xmla.PROP_DATASOURCEINFO] = datasource[Xmla.PROP_DATASOURCEINFO];
			//properties[Xmla.PROP_CATALOG] = getSelectValue(select_catalog);
			properties[Xmla.PROP_CATALOG] = "[LdCatalogSchema]";

			var restrictions = {};
			restrictions["CATALOG_NAME"] = getSelectValue(select_catalog);
			restrictions["CUBE_NAME"] = getSelectValue(select_cube);

			xmla.discoverMDMeasures({
				url : xmlaurl,
				properties : properties,
				restrictions : restrictions
			});
		}

		function select_hierarchies_onchange() {
			var prefix = "select_hierarchies_";
			var option = this.options[this.selectedIndex];
			option.selected = false;
			var form = this.form;
			var elements = form.elements;
			var axis;
			var axis_buttons = elements["axis"];
			for ( var i = 0; i < axis_buttons.length; i++) {
				if (axis_buttons[i].checked) {
					axis = axis_buttons[i].value;
					break;
				}
			}
			if (!axis) {
				throw "Whoops...expected one axis radiobutton to be checked";
			}
			var hierarchy = option.value;
			var select_hierarchy = prefix + axis;
			if (select_hierarchy == this.id) {
				alert("You cannot move the [" + hierarchy
						+ "] hierarchy to the [" + axis + "] axis, "
						+ "because it's there already.\n"
						+ "Use the radio buttons to select another axis.");
			} else {
				this.removeChild(option);
				this.size = this.options.length;
				var target = document.getElementById(select_hierarchy);
				target.appendChild(option);
				target.size = target.options.length;
			}
		}

		function select_measures_onchange() {
			var prefix = "select_measures_";
			var option = this.options[this.selectedIndex];
			option.selected = false;
			var select;
			switch (this) {
			case select_measures_unused:
				select = select_measures_used;
				break;
			case select_measures_used:
				select = select_measures_unused;
				break;
			}
			this.removeChild(option);
			this.size = this.options.length;
			select.appendChild(option);
			select.size = select.options.length;
			
			// Now, we could compute the table again
			// button_render_table_onclick();
		}

		function select_empty_cells_onchange() {
			var model = loadModel(xmla.responseXML);
			var mdx = getMDX(model);
			executeMDX(mdx);
		}

		function button_render_table_onclick() {
			
			// Reset Loader Gif
			image_ajax_loader_update.innerHTML = "<img src=\"lib/xmla4js/css/ajax-loader.gif\" /><br/><br/>";
			
			if (!select_measures_used.options.length) {
				alert("Please add one or more measures to the used list.");
				return;
			}
			if (!select_hierarchies_columns.options.length) {
				alert("Please add one or more hierarchies to the Columns axis.");
				return;
			}
			var mdx = generateMDX();
			
			// Track event
			ga('send', 'event', 'button_update_table', uuid, mdx, 1);
			
			executeMDX(mdx);
		}
		
		function button_show_log_onclick() {
			
			if (show_log_text.style.display == "") {
				show_log_text.style.display = "none";
			} else {
				show_log_text.style.display = "";	
			}
		}
		
		function button_show_examples_onclick() {
			
			if (show_examples_text.style.display == "") {
				show_examples_text.style.display = "none";
			} else {
				show_examples_text.style.display = "";	
			}
		}

		function executeMDX(mdx) {
			section_query.innerHTML = mdx;
			var datasource = datasources[select_datasource.selectedIndex];
			var catalog = getSelectValue(select_catalog);
								
			xmla.executeMultiDimensional({
				url : xmlaurl,
				statement : mdx,
				properties : {
					DataSourceInfo : datasource["DataSourceInfo"],
					Catalog : catalog
				}
			});
		}

		var _getElementsByTagNameNS = function(node, ns, prefix, tagName) {
			if (typeof (node.getElementsByTagNameNS) == "function") {
				return node.getElementsByTagNameNS(ns, tagName);
			} else if (prefix) {
				return node.getElementsByTagName(prefix + ":" + tagName);
			} else {
				return node.getElementsByTagName(tagName);
			}
		};

		function loadModel(node) {
			var _xmlnsResultset = "urn:schemas-microsoft-com:xml-analysis:mddataset";
			var model = {
				type : "mdx",
				axes : {},
				cube : _getElementsByTagNameNS(node, _xmlnsResultset, null,
						"CubeName").item(0).firstChild.data
			};
			for ( var axes = _getElementsByTagNameNS(node, _xmlnsResultset,
					null, "Axis"), axisNode, axis, i = 0, numAxes = axes.length; i < numAxes; i++) {
				axisNode = axes.item(i);
				axis = {
					type : "set",
					tuples : [],
					name : axisNode.getAttribute("name")
				};
				model.axes[axis.name] = axis;
				for ( var tuples = _getElementsByTagNameNS(axisNode,
						_xmlnsResultset, null, "Tuple"), tupleNode, tuple, j = 0, numTuples = tuples.length; j < numTuples; j++) {
					tupleNode = tuples.item(j);
					tuple = {
						type : "tuple",
						members : []
					};
					axis.tuples.push(tuple);
					for ( var members = _getElementsByTagNameNS(tupleNode,
							_xmlnsResultset, null, "Member"), memberNode, k = 0, numMembers = members.length; k < numMembers; k++) {
						memberNode = members.item(k);
						member = {
							type : "member",
							hierarchy : memberNode.getAttribute("Hierarchy")
						};
						for ( var childNodes = _getElementsByTagNameNS(
								memberNode, _xmlnsResultset, null, "*"), childNode, l = 0, numChildNodes = childNodes.length; l < numChildNodes; l++) {
							childNode = childNodes.item(l);
							member[childNode.tagName] = childNode.firstChild.data;
						}
						tuple.members.push(member);
					}
				}
			}
			return model;
		}

		function transformModel(model, drillTuple, memberIndex, expanded) {
			var memberFound, drillMember = drillTuple[memberIndex];
			var axes = model.axes;
			for ( var p in axes) {
				if (axes.hasOwnProperty(p)) {
					var axis = axes[p];
					for ( var tuples = axis.tuples, numTuples = tuples.length, i = 0; i < numTuples; i++) {
						memberFound = true;
						var tuple = tuples[i];
						for ( var members = tuple.members, numMembers = members.length, j = 0; j < numMembers; j++) {
							var member = members[j];
							if (memberFound && (member.UName != drillTuple[j])) {
								memberFound = false;
							}
						}
						if (memberFound) {
							if (expanded) { //rollup
								//advance, skippin all 'child' tuples.
								var offset = i + 1;
								while (++i < numTuples) {
									members = tuples[i].members;
									for ( var k = 0; k < memberIndex; k++) {
										if (drillTuple[k].Uname != members[k].Uname) {
											break;
										}
									}
									if (members[memberIndex].UName
											.indexOf(drillMember) != 0) {
										break;
									}
								}
								;
								tuples.splice(offset, i - offset);
							} else { //drilldown
								var expr = {
									type : "expression",
									text : drillMember + ".Children"
								};
								if (members.length > memberIndex + 1) {
									expr = {
										type : "function",
										name : "crossjoin",
										args : [
												expr,
												{
													type : "set",
													tuples : [ {
														type : "tuple",
														members : members
																.slice(memberIndex + 1)
													} ]
												} ]
									}
								}
								if (memberIndex) {
									expr = {
										type : "function",
										name : "crossjoin",
										args : [
												{
													type : "set",
													tuples : [ {
														type : "tuple",
														members : members
																.slice(0,
																		memberIndex)
													} ]
												}, expr ]
									}
								}
								if (tuples.length > i + 1) {
									expr = {
										type : "function",
										name : "union",
										args : [ expr, {
											type : "set",
											tuples : tuples.slice(i + 1)
										} ]
									}
								}
								expr = {
									type : "function",
									name : "union",
									args : [ {
										type : "set",
										tuples : tuples.slice(0, i + 1)
									}, expr ]
								}
								axes[p] = expr;
							}
							return model;
						}
					}
				}
			}
			return model;
		}

		function getMDX(node) {
			var type = node.type;
			var mdx = "";
			switch (type) {
			case "mdx":
				mdx = "SELECT ";
				var axis, axes = node.axes, emptyCells = getSelectValue(select_empty_cells);
				for ( var i = 0; true; i++) {
					axis = axes["Axis" + i];
					if (!axis) {
						break;
					}
					if (i > 0) {
						mdx += ",\n";
					}
					if (emptyCells === "all" || emptyCells === "cols"
							&& i === 0 || emptyCells === "rows" && i === 1) {
						mdx += "NON EMPTY ";
					}
					mdx += getMDX(axis) + " ON Axis(" + i + ")";
				}
				mdx += "\nFROM [" + node.cube + "]";
				axis = axes["SlicerAxis"];
				if (axis) {
					mdx += "\nWHERE " + getMDX(axis);
				}
				break;
			case "set":
				var tuple;
				var tuples = node.tuples, numTuples = tuples.length, i;
				for (i = 0; i < numTuples; i++) {
					tuple = tuples[i];
					if (mdx != "" && tuple.members.length) {
						mdx += ",\n";
					}
					if (tuple.members.length) {
						mdx += getMDX(tuple);
					}
				}
				mdx = "{" + mdx + "}";
				break;
			case "tuple":
				var members = node.members, numMembers = members.length, i;
				for (i = 0; i < numMembers; i++) {
					if (i > 0) {
						mdx += ",\n";
					}
					mdx += getMDX(members[i]);
				}
				mdx = "(" + mdx + ")";
				break;
			case "member":
				mdx = node.UName;
				break;
			case "function":
				mdx = node.name + "(";
				var args = node.args, numArgs = args.length, i;
				for (i = 0; i < numArgs; i++) {
					if (i > 0) {
						mdx += ",\n";
					}
					mdx += getMDX(args[i]);
				}
				mdx += ")";
				break;
			case "expression":
				mdx = node.text;
				break;
			default:
				throw "Unknown type: " + type;
			}
			return mdx;
		}

		function drill(tuple, memberIndex, expanded) {
			var model = loadModel(xmla.responseXML);
			model = transformModel(model, tuple, memberIndex, expanded);
			var mdx = getMDX(model);
			executeMDX(mdx);
		}
		
		function exampledataseturi_onclick() {
			dsurl.value = this.attributes.value;
		}
		
		function s4() {
			  return Math.floor((1 + Math.random()) * 0x10000)
			             .toString(16)
			             .substring(1);
		};

		function guid() {
		  return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
		         s4() + '-' + s4() + s4() + s4();
		}

		/*
		 *   Initialization
		 */
		function init() {

			initAjaxLoader();
			initDiscoverEvents();
			initExecuteEvents();
			
			button_discover_datasources.onclick = button_discover_datasources_onclick;
			button_render_table.onclick = button_render_table_onclick;
			button_show_log.onclick = button_show_log_onclick;
			button_show_examples.onclick = button_show_examples_onclick;

			select_datasource.onchange = select_datasource_onchange;
			select_catalog.onchange = select_catalog_onchange;
			select_cube.onchange = select_cube_onchange;

			select_measures_unused.onchange = select_measures_onchange;
			select_measures_used.onchange = select_measures_onchange;

			select_hierarchies_slicer.onchange = select_hierarchies_onchange;
			select_hierarchies_rows.onchange = select_hierarchies_onchange;
			select_hierarchies_columns.onchange = select_hierarchies_onchange;

			select_empty_cells.onchange         = select_empty_cells_onchange;
			
        }

        init();
	</script>
</body>
</html>
